<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <div id="text"></div>
    <div id="foo">
        <span></span>
        <div id="bar"></div>
    </div>
</div>
<script>
  const bucket = new WeakMap()

  const track = (target, key) => {
    if (!activeEffect) return
    let depsMap = bucket.get(target)

    if (!depsMap) {
      depsMap = new Map()
      bucket.set(target, depsMap)
    }

    let deps = depsMap.get(key)
    if (!deps) {
      deps = new Set()
      depsMap.set(key, deps)
    }

    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }

  const trigger = (target, key) => {
    const depsMap = bucket.get(target)
    if (!depsMap) return
    const effects = depsMap.get(key)
    const effectsToRun = new Set()
    effects && effects.forEach(effectFn => {
      if (activeEffect !== effectFn)
        effectsToRun.add(effectFn)
    })
    effectsToRun && effectsToRun.forEach(effectFn => effectFn())
  }

  const cleanup = (effectFn) => {
    for (let i = 0; i < effectFn.deps.length; i++) {
      effectFn.deps[i].delete(effectFn)
    }

    effectFn.deps.length = 0
  }

  let activeEffect = null
  const effectsBucket = []
  const effect = (fn) => {
    const effectFn = () => {
      cleanup(effectFn)
      activeEffect = effectFn
      effectsBucket.push(effectFn)
      fn()
      effectsBucket.pop()
      activeEffect = effectsBucket[effectsBucket.length - 1]
    }
    effectFn.deps = []
    effectFn()
  }

  const data = {
    ok: true,
    text: 'hello world',
    foo: 1,
    bar: 2
  }

  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newValue) {
      target[key] = newValue
      trigger(target, key)
      return true
    }
  })

  function effectFn() {
    console.log('副作用函数执行...')
    document.getElementById('text').innerText = obj.ok ? obj.text : "not"
  }

  function effectFn1() {
    console.log('副作用函数1执行...')

    function effectFn2() {
      console.log('副作用函数2执行...')
      document.getElementById('bar').innerText = obj.bar
    }

    effect(effectFn2)
    document.querySelector('#foo span').innerHTML = obj.foo
  }

  effect(effectFn)
  effect(effectFn1)
  effect(() => {
    obj.bar++
  })

  setTimeout(() => {
    obj.text = 'hello vue'
  }, 1000)

  setTimeout(() => {
    obj.ok = false
  }, 2000)

  setTimeout(() => {
    obj.text = 'not effect'
  }, 3000)

  setTimeout(() => {
    obj.foo = 3
  }, 4000)
</script>
</body>
</html>